/* Copyright (c) 2010, mitsuhisa.net
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of the misuhisa.net nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <cstdlib>
#include <cassert>
#include "String.hpp"
#include "StrPool.hpp"
#include "BuiltinPrototype.hpp"

namespace nyu {

String::String(unsigned int h) : hash_(h) {
}

String::~String() {
}

Value String::getSlot(Value name, Context& c) {
    if (String::PROTO->opEq(name, c))
        return object_to_value(BuiltinPrototype::STRING);

    return BuiltinPrototype::STRING->getSlot(name, c);
}

void String::putSlot(Value name, Value value, Context& c) {
    // TODO
}

unsigned int String::hash() {
    return hash_;
}

String* String::toString(Context& c) {
    ref();
    return this;
}

bool String::opEq(Value v, Context& c, bool is_tail) {
    if (object_to_value(this) == v)
        return true;

    if (!is_object(v) || is_null(v))
        return false;

    return value_to_object(v)->revOpEq(this, c, is_tail);
}

int_type String::length(Context& c) {
    return ::strlen(strval());
}

bool String::revOpEq(String* other, Context& c, bool is_tail) {
    assert(other);

    return ::strcmp(other->strval(), strval()) == 0;
}

unsigned int String::calcHash(const char* str) {
    unsigned int h = 0;

    for (const char* p = str; *p; ++p) {
        h = 31 * h + static_cast<unsigned char> (*p);
    }

    return h;
}

PooledString::PooledString(char* value, unsigned int h) :
        String(h), value_(value) {
}

PooledString::~PooledString() {
    ::free(value_);
}

const char* PooledString::strval() const {
    return value_;
}

DynamicString::DynamicString(const std::string& value) :
        String(0), value_(value) {
}

unsigned int DynamicString::hash() {
    return hash_ ? hash_ : (hash_ = calcHash(value_.c_str()));
}

const char* DynamicString::strval() const {
    return value_.c_str();
}

int_type DynamicString::length(Context& c) {
    return value_.length();
}

StaticString::StaticString(const char* value) :
        String(calcHash(value)), value_(value) {
}

const char* StaticString::strval() const {
    return value_;
}

void String::initialize() {
    PROTO = StrPool::get("__proto__");

    OBJECT = StrPool::get("Object");
    STRING = StrPool::get("String");
    ARRAY = StrPool::get("Array");
    RANGE = StrPool::get("Range");
}

void String::finalize() {
    RANGE->unref();
    RANGE = 0;

    ARRAY->unref();
    ARRAY = 0;

    STRING->unref();
    STRING = 0;

    OBJECT->unref();
    OBJECT = 0;

    PROTO->unref();
    PROTO = 0;
}

String* String::PROTO = 0;

String* String::OBJECT = 0;
String* String::STRING = 0;
String* String::ARRAY = 0;
String* String::RANGE = 0;

}
